#ifndef EPUBDUKPT_COMM_FORMAT_HPP
#define EPUBDUKPT_COMM_FORMAT_HPP

SJR_NAMESPACE_BEGIN

class EpubFormat final {
public:
  constexpr static int BUFFER_SIZE               = 2048;
  constexpr static char FORMAT_ARRAY_TO_STRING[] = "%02X";

  template<class... Args>
  static auto format(std::string const &format, Args... args) -> std::string {
    return EpubFormat::instance().toFormat(format, args...);
  }

  static auto string(unsigned char const *in, int inLen) -> std::string {
    return EpubFormat::instance().toString(in, inLen);
  }

  static auto array(std::string const &in) -> std::vector<unsigned char> {
    return EpubFormat::instance().toArray(in);
  }

private:
  char mBuffer[BUFFER_SIZE];
  std::vector<unsigned char> mArray;

  EpubMutex mSync;

  EpubFormat() : mBuffer(), mArray(), mSync() {}

  static auto instance() -> EpubFormat & {
    static EpubFormat *obj = nullptr;
    if (obj == nullptr)
      obj = new EpubFormat();
    return (*obj);
  }

  template<class... Args>
  auto toFormat(std::string const &format, Args... args) -> std::string {
    EpubUniqueLock lock(mSync);
    snprintf(mBuffer, sizeof(mBuffer), format.c_str(), args...);
    return mBuffer;
  }

  template<class... Args>
  auto toString(unsigned char const *in, int inLen) -> std::string {
    EpubUniqueLock lock(mSync);
    mBuffer[0]    = 0x00;
    auto &&bufLen = static_cast<int>(sizeof(mBuffer));
    auto &&retLen = bufLen > inLen ? inLen : bufLen - 1;

    for (int i = 0; i < retLen; ++i)
      snprintf(&mBuffer[i * 2], (bufLen - i * 2), FORMAT_ARRAY_TO_STRING, in[i]);
    return mBuffer;
  }

  auto toArray(std::string const &in) -> std::vector<unsigned char> {
    EpubUniqueLock lock(mSync);
    int j        = 0;
    auto &&inLen = in.size();

    if ((inLen % 2) != 0)
      ++inLen;

    mArray.resize(inLen / 2);
    memset(&mArray[0], 0, mArray.size());

    for (int i = 0; i < in.size(); ++i) {
      if ((i != 0) && (i % 2) == 0)
        ++j;

      if ((in[i] >= 0x30u) && (in[i] <= 0x39u) && ((i % 2) == 0))
        mArray[j] = (in[i] - 0x30u) << 4u;
      else if ((in[i] >= 0x41u) && (in[i] <= 0x46u) && ((i % 2) == 0))
        mArray[j] = (in[i] - 0x37u) << 4u;
      else if ((in[i] >= 0x61u) && (in[i] <= 0x66u) && ((i % 2) == 0))
        mArray[j] = (in[i] - 0x57u) << 4u;
      else if ((in[i] >= 0x30u) && (in[i] <= 0x39u) && ((i % 2) != 0))
        mArray[j] += in[i] - 0x30u;
      else if ((in[i] >= 0x41u) && (in[i] <= 0x46u) && ((i % 2) != 0))
        mArray[j] += in[i] - 0x37u;
      else if ((in[i] >= 0x61u) && (in[i] <= 0x66u) && ((i % 2) != 0))
        mArray[j] += in[i] - 0x57u;
    }
    return mArray;
  }
};

SJR_NAMESPACE_END

#endif//EPUBDUKPT_COMM_FORMAT_HPP
